#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/Xutil.h>
#include <thread>
#include <vector>
#include <chrono>
#define STB_IMAGE_IMPLEMENTATION
#define STB_IMAGE_STATIC
#include <zeno3rd/stb_image.h>
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
#define INCBIN_PREFIX incbin_
#include <zeno3rd/incbin.h>

INCBIN(splashscreenbmp, "splash.bmp");

static void splashscreen() {
    Display *display;
    Window window;
    XEvent event;
    XImage *image;
    int screen_num;
    unsigned long black_pixel, white_pixel;
    int w, h, comp = 3;

    unsigned char *bmpa = stbi_load_from_memory(incbin_splashscreenbmp_data, incbin_splashscreenbmp_size, &w, &h, &comp, comp);
    if (!bmpa) return;
    auto imga = (unsigned char *)malloc(w * h * 4);
    unsigned char *bmpp = bmpa, *imgp = imga;
    for (size_t i = 0; i < w * h; i++) {
        *imgp++ = bmpp[2];
        *imgp++ = bmpp[1];
        *imgp++ = bmpp[0];
        *imgp++ = 255;
        bmpp += 3;
    }
    stbi_image_free(bmpa);

    display = XOpenDisplay(NULL);
    if (display == NULL) {
        return;
    }

    screen_num = DefaultScreen(display);
    black_pixel = BlackPixel(display, screen_num);
    white_pixel = WhitePixel(display, screen_num);

    window = XCreateSimpleWindow(display, RootWindow(display, screen_num),
                                 0, 0, w, h, 1,
                                 black_pixel, white_pixel);

    struct MwmHints {
        unsigned long flags;
        unsigned long functions;
        unsigned long decorations;
        long input_mode;
        unsigned long status;
    };
    enum {
        MWM_HINTS_FUNCTIONS = (1L << 0),
        MWM_HINTS_DECORATIONS =  (1L << 1),
        MWM_FUNC_ALL = (1L << 0),
        MWM_FUNC_RESIZE = (1L << 1),
        MWM_FUNC_MOVE = (1L << 2),
        MWM_FUNC_MINIMIZE = (1L << 3),
        MWM_FUNC_MAXIMIZE = (1L << 4),
        MWM_FUNC_CLOSE = (1L << 5)
    };
    Atom mwmHintsProperty = XInternAtom(display, "_MOTIF_WM_HINTS", 0);
    struct MwmHints hints;
    hints.flags = MWM_HINTS_DECORATIONS;
    hints.decorations = 0;
    XChangeProperty(display, window, mwmHintsProperty, mwmHintsProperty, 32,
                    PropModeReplace, (unsigned char *)&hints, 5);

    image = XCreateImage(display, DefaultVisual(display, screen_num),
                         DefaultDepth(display, screen_num), ZPixmap, 0,
                         (char *)imga, w, h, 32, 0);
    if (image == NULL) {
        free(imga);
        return;
    }

    Atom wmDeleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", False);
    XSetWMProtocols(display, window, &wmDeleteMessage, 1);

    XSelectInput(display, window, ExposureMask | FocusChangeMask);
    XStoreName(display, window, "ZENO");
    XMapWindow(display, window);

    while (true) {
        XNextEvent(display, &event);
        if (event.type == Expose && event.xexpose.count == 0) {
            XPutImage(display, window, DefaultGC(display, screen_num),
                      image, 0, 0, 0, 0, image->width, image->height);
        } else if (event.type == FocusOut) {
            /* for (size_t n = image->width * image->height * 4, i = 3; i < n; i += 4) { */
            /*     image->data[i] = (char)((unsigned char)image->data[i] / 2); */
            /* } */
            XUnmapWindow(display, window);
        } else if (event.type == ClientMessage &&
            (Atom)event.xclient.data.l[0] == wmDeleteMessage) {
            XDestroyImage(image);
            XDestroyWindow(display, window);
            XCloseDisplay(display);
            std::_Exit(1);
        }
        while (!XPending(display)) {
            std::this_thread::sleep_for(std::chrono::milliseconds(50));
        }
    }
    XDestroyImage(image);
    XDestroyWindow(display, window);
    XCloseDisplay(display);
    free(imga);
}

static int dummy = (std::thread(splashscreen).detach(), 0);
